/* 
 * Contributors: Youngjae Kim (youkim@cse.psu.edu)
 *               Aayush Gupta (axg354@cse.psu.edu_
 *   
 * In case if you have any doubts or questions, kindly write to: youkim@cse.psu.edu 
 *
 * This source plays a role as bridiging disksim and flash simulator. 
 * 
 * Request processing flow: 
 *
 *  1. Request is sent to the simple flash device module. 
 *  2. This interface determines FTL type. Then, it sends the request
 *     to the lower layer according to the FTL type. 
 *  3. It returns total time taken for processing the request in the flash. 
 *
 */

#include "ssd_interface.h"
#include "disksim_global.h"
#include "dftl.h"
#include "stdio.h"


extern int merge_switch_num;
extern int merge_partial_num;
extern int merge_full_num;
int old_merge_switch_num = 0;
int old_merge_partial_num = 0;
int old_merge_full_num= 0;
int old_flash_gc_read_num = 0;
int old_flash_erase_num = 0;
int req_count_num = 1;
int cache_hit, rqst_cnt;

int cache_cmt_hit;
int cache_scmt_hit;
int cache_slcmt_hit;

int flag1 = 1;
int count = 0;
int update_flag = 0;//shzb:用于判断连续缓存中的部分映射项更新标志是否置零
int translation_read_num = 0;
int translation_write_num = 0;


int MLC_page_num_for_2nd_map_table;
int nand_SLC_page_num;

//shzb:odftl
int indexofseq = 0,indexofarr = 0;
int indexold = 0,indexnew = 0,index2nd=0;
int maxentry=0,MC=0;//分别为出现最多的项的值和出现的次数
 
int in_cache_count;
//du:自适应
#define itemcount_threshold 1  //trace的阈值

int write_cnt=0;
int read_cnt=0;
int request_cnt=0;
double write_ratio=0;
double read_ratio=0;
// 全局变量，统计写入多少次的页？
int total_request_size=0;
int itemcount=0;
double average_request_size=0;

#define CACHE_MAX_ENTRIES 300

#define THRESHOLD 2
// 单次预读的数据页个数
#define NUM_ENTRIES_PER_TIME 8
//duchenjie:判断MC
#define Threshold 100
//翻译页的初始化
#define init_size 32
#define increment 8

int sequential_count=0;
int big_request_entry = 0;
int big_request_count = 0;


int MAP_REAL_MAX_ENTRIES=0;
//int MAP_GHOST_MAX_ENTRIES=0;
int MAP_SEQ_MAX_ENTRIES=0; 
int MAP_SECOND_MAX_ENTRIES=0; 
int *real_arr= NULL;

//int *ghost_arr=NULL;
int *seq_arr= NULL;
int *second_arr=NULL;
int SLC_write_page_count=0;
int cache_arr[CACHE_MAX_ENTRIES];




/***********************************************************************
  Variables for statistics    
 ***********************************************************************/
unsigned int cnt_read = 0;
unsigned int cnt_write = 0;
unsigned int cnt_delete = 0;
unsigned int cnt_evict_from_flash = 0;
unsigned int cnt_evict_into_disk = 0;
unsigned int cnt_fetch_miss_from_disk = 0;
unsigned int cnt_fetch_miss_into_flash = 0;

double sum_of_queue_time = 0.0;
double sum_of_service_time = 0.0;
double sum_of_response_time = 0.0;
unsigned int total_num_of_req = 0;

/***********************************************************************
  Mapping table
 ***********************************************************************/
int real_min = -1;
int real_max = 0;
/*
int second_min = -1;
int second_max = 0;
//duchenjie:slcmt
int block_min = -1;
int block_max = 0;
struct lru *block;
//memset(blk,0xFF,INIT_SIZE*sizeof(struct lru));
int block_index = 0;
int block_entry_num = 0;
int blocksize = init_size;//LRU块列表中初始块的个数*/
/***********************************************************************
  Cache
 ***********************************************************************/
int cache_min = -1;
int cache_max = 0;

// Interface between disksim & fsim 

void reset_flash_stat()
{
  flash_read_num =0;
  flash_write_num  =0;
  flash_gc_read_num = 0;
  flash_gc_write_num = 0; 
  flash_erase_num =0;
  flash_oob_read_num  =0;
  flash_oob_write_num  =0; 
}
void reset_SLC_flash_stat()
{
  SLC_flash_read_num  =0;
  SLC_flash_write_num  =0;
  SLC_flash_gc_read_num  = 0;
  SLC_flash_gc_write_num  = 0; 
  SLC_flash_erase_num  =0;
  SLC_flash_oob_read_num  =0;
  SLC_flash_oob_write_num  =0; 
}
void reset_MLC_flash_stat()
{
  MLC_flash_read_num =0;
  MLC_flash_write_num =0;
  MLC_flash_gc_read_num = 0;
  MLC_flash_gc_write_num = 0; 
  MLC_flash_erase_num =0;
  MLC_flash_oob_read_num =0;
  MLC_flash_oob_write_num =0; 
}
FILE *fp_flash_stat;
FILE *fp_gc;
FILE *fp_gc_timeseries;
// gc_di统计的时没有垃圾回收的时延，gc_ti统计的是有垃圾回收时的时延
double gc_di =0 ,gc_ti=0;


double calculate_delay_flash()
{
  double delay;
  double read_delay, write_delay;
  double erase_delay;
  double gc_read_delay, gc_write_delay;
  double oob_write_delay, oob_read_delay;

  oob_read_delay  = (double)OOB_READ_DELAY  * flash_oob_read_num;
  oob_write_delay = (double)OOB_WRITE_DELAY * flash_oob_write_num;

  read_delay     = (double)READ_DELAY  * flash_read_num; 
  write_delay    = (double)WRITE_DELAY * flash_write_num; 
  erase_delay    = (double)ERASE_DELAY * flash_erase_num; 

  gc_read_delay  = (double)GC_READ_DELAY  * flash_gc_read_num; 
  gc_write_delay = (double)GC_WRITE_DELAY * flash_gc_write_num; 


  delay = read_delay + write_delay + erase_delay + gc_read_delay + gc_write_delay + 
    oob_read_delay + oob_write_delay;

  if( flash_gc_read_num > 0 || flash_gc_write_num > 0 || flash_erase_num > 0 ) {
    gc_ti += delay;
  }
  else {
    gc_di += delay;
  }

  if(warm_done == 1){
    fprintf(fp_gc_timeseries, "%d\t%d\t%d\t%d\t%d\t%d\n", 
      req_count_num, merge_switch_num - old_merge_switch_num, 
      merge_partial_num - old_merge_partial_num, 
      merge_full_num - old_merge_full_num, 
      flash_gc_read_num,
      flash_erase_num);

    old_merge_switch_num = merge_switch_num;
    old_merge_partial_num = merge_partial_num;
    old_merge_full_num = merge_full_num;
    req_count_num++;
  }

  reset_flash_stat();

  return delay;
}
double calculate_delay_SLC_flash()
{
  double delay;
  double read_delay, write_delay;
  double erase_delay;
  double gc_read_delay, gc_write_delay;
  double oob_write_delay, oob_read_delay;

  oob_read_delay  = (double)SLC_OOB_READ_DELAY  * SLC_flash_oob_read_num;
  oob_write_delay = (double)SLC_OOB_WRITE_DELAY * SLC_flash_oob_write_num;

  read_delay     = (double)SLC_READ_DELAY  * SLC_flash_read_num; 
  write_delay    = (double)SLC_WRITE_DELAY * SLC_flash_write_num; 
  erase_delay    = (double)SLC_ERASE_DELAY * SLC_flash_erase_num; 

  gc_read_delay  = (double)SLC_GC_READ_DELAY  * SLC_flash_gc_read_num; 
  gc_write_delay = (double)SLC_GC_WRITE_DELAY * SLC_flash_gc_write_num; 


  delay = read_delay + write_delay + erase_delay + gc_read_delay + gc_write_delay + 
    oob_read_delay + oob_write_delay;

  if( SLC_flash_gc_read_num > 0 || SLC_flash_gc_write_num > 0 || SLC_flash_erase_num > 0 ) {
    gc_ti += delay;
  }
  else {
    gc_di += delay;
  }

  if(warm_done == 1){
    fprintf(fp_gc_timeseries, "%d\t%d\t%d\t%d\t%d\t%d\n", 
      req_count_num, merge_switch_num - old_merge_switch_num, 
      merge_partial_num - old_merge_partial_num, 
      merge_full_num - old_merge_full_num, 
      flash_gc_read_num,
      flash_erase_num);

    old_merge_switch_num = merge_switch_num;
    old_merge_partial_num = merge_partial_num;
    old_merge_full_num = merge_full_num;
    req_count_num++;
  }

  reset_SLC_flash_stat();

  return delay;
}
double calculate_delay_MLC_flash()
{
  double delay;
  double read_delay, write_delay;
  double erase_delay;
  double gc_read_delay, gc_write_delay;
  double oob_write_delay, oob_read_delay;

  oob_read_delay  = (double)MLC_OOB_READ_DELAY  * MLC_flash_oob_read_num;
  oob_write_delay = (double)MLC_OOB_WRITE_DELAY * MLC_flash_oob_write_num;

  read_delay     = (double)MLC_READ_DELAY  * MLC_flash_read_num; 
  write_delay    = (double)MLC_WRITE_DELAY * MLC_flash_write_num; 
  erase_delay    = (double)MLC_ERASE_DELAY * MLC_flash_erase_num; 

  gc_read_delay  = (double)MLC_GC_READ_DELAY  * MLC_flash_gc_read_num; 
  gc_write_delay = (double)MLC_GC_WRITE_DELAY * MLC_flash_gc_write_num; 


  delay = read_delay + write_delay + erase_delay + gc_read_delay + gc_write_delay + 
    oob_read_delay + oob_write_delay;

  if( MLC_flash_gc_read_num > 0 || MLC_flash_gc_write_num > 0 || MLC_flash_erase_num > 0 ) {
    gc_ti += delay;
  }
  else {
    gc_di += delay;
  }

  if(warm_done == 1){
    fprintf(fp_gc_timeseries, "%d\t%d\t%d\t%d\t%d\t%d\n", 
      req_count_num, merge_switch_num - old_merge_switch_num, 
      merge_partial_num - old_merge_partial_num, 
      merge_full_num - old_merge_full_num, 
      flash_gc_read_num,
      flash_erase_num);

    old_merge_switch_num = merge_switch_num;
    old_merge_partial_num = merge_partial_num;
    old_merge_full_num = merge_full_num;
    req_count_num++;
  }

  reset_MLC_flash_stat();

  return delay;
}

/***********************************************************************
  Initialize Flash Drive 
  ***********************************************************************/

void initFlash()
{
  blk_t total_blk_num;
  blk_t total_util_blk_num; //表示当前未使用的块（用户可用的块）
  blk_t total_extr_blk_num;
  /* 添加的变量*/
  blk_t total_SLC_blk_num;
  blk_t total_MLC_blk_num;
  /* 添加的变量*/  
  blk_t total_SLC_util_blk_num;
  blk_t total_SLC_extr_blk_num;
  blk_t total_MLC_util_blk_num;
  blk_t total_MLC_extr_blk_num;
  /**************/

  //SLC和MLC的块加起来就是全部的块 
  // total number of sectors    
  total_util_sect_num  = flash_numblocks;
  total_extra_sect_num = flash_extrblocks;
  total_sect_num = total_util_sect_num + total_extra_sect_num;

  /* 添加的变量*/    
  total_MLC_util_sect_num  = total_util_sect_num-total_SLC_util_sect_num;
  total_MLC_extra_sect_num = total_extra_sect_num-total_SLC_extra_sect_num;
    /* 添加的变量*/  
  total_SLC_sect_num=total_SLC_util_sect_num+total_SLC_extra_sect_num;
  total_MLC_sect_num=total_MLC_util_sect_num+total_MLC_extra_sect_num; 

  // total number of blocks 
  total_SLC_blk_num  = total_SLC_sect_num / S_SECT_NUM_PER_BLK;     
  total_MLC_blk_num  = total_MLC_sect_num / M_SECT_NUM_PER_BLK;
  total_blk_num =total_SLC_blk_num + total_MLC_blk_num; 

  total_SLC_util_blk_num = total_SLC_util_sect_num / S_SECT_NUM_PER_BLK;    
  total_MLC_util_blk_num = total_MLC_util_sect_num / M_SECT_NUM_PER_BLK;
  total_SLC_extr_blk_num = total_SLC_extra_sect_num / S_SECT_NUM_PER_BLK;
  total_MLC_extr_blk_num = total_MLC_extra_sect_num / M_SECT_NUM_PER_BLK;
  total_util_blk_num = total_SLC_util_blk_num + total_MLC_util_blk_num;
  global_total_blk_num = total_util_blk_num;

  total_extr_blk_num = total_blk_num - total_util_blk_num;        // total extra block number

  ASSERT(total_extr_blk_num != 0);

  if (nand_init(total_blk_num,total_SLC_blk_num,total_MLC_blk_num, 3) < 0) {
    EXIT(-4); 
  }

  switch(ftl_type){

    // pagemap
    case 1: ftl_op = pm_setup(); break;
    // blockmap
    //case 2: ftl_op = bm_setup(); break;
    // o-pagemap 
    case 3: ftl_op = opm_setup(); break;
    // fast
    case 4: ftl_op = lm_setup(); break;

    default: break;
  }

  ftl_op->init(total_SLC_util_blk_num,total_MLC_util_blk_num, total_extr_blk_num);

  nand_stat_reset();
}

void printWearout()
{
  int i;
  FILE *fp = fopen("wearout", "w");
  
  for(i = 0; i<nand_blk_num; i++)
  {
    fprintf(fp, "%d %d\n", i, nand_blk[i].state.ec); 
  }

  fclose(fp);
}


void endFlash()
{
  nand_stat_print(outputfile);
  ftl_op->end;
  nand_end();
}  

/***********************************************************************
  Send request (lsn, sector_cnt, operation flag)
  ***********************************************************************/

// 添加了两个变量flash_flag和region_flag两个输入的参数
void send_flash_request(int start_blk_no, int block_cnt, int operation, int mapdir_flag,int flash_flag,int region_flag)
{
	int size;
	//size_t (*op_func)(sect_t lsn, size_t size);
	
  // 函数指针声明
  size_t (*op_func)(sect_t lsn, size_t size, int mapdir_flag,int region_flag);

// 错误调试信息输出
  if((start_blk_no + block_cnt) >= total_util_sect_num){
    printf("start_blk_no: %d, block_cnt: %d, total_util_sect_num: %d\n", 
        start_blk_no, block_cnt, total_util_sect_num);
    exit(0);
  }
// flash_flag=0写入到SLC中，flash！=0 写入到MLC中
 if(flash_flag==0){
	   switch(operation){

	   //write
	   case 0:
// 对应的ftl算法的文件增加对应的函数ftl_op->Sopm_write
		op_func = ftl_op->Sopm_write;
		while (block_cnt> 0) {
			size = op_func(start_blk_no, block_cnt, mapdir_flag,region_flag);
			start_blk_no += size;
			block_cnt-=size;
		}
		break;
	  //read
	  case 1:


		op_func = ftl_op->Sopm_read;
		while (block_cnt> 0) {
			size = op_func(start_blk_no, block_cnt, mapdir_flag,region_flag);
			start_blk_no += size;
			block_cnt-=size;
		}
		break;

	  default: 
		break;
	}
 }else{
         switch(operation){

	   //write
	   case 0:

		op_func = ftl_op->Mopm_write;
		while (block_cnt> 0) {
			size = op_func(start_blk_no, block_cnt, mapdir_flag,region_flag);
			start_blk_no += size;
			block_cnt-=size;
		}
		break;
	  //read
	  case 1:

		op_func = ftl_op->Mopm_read;
		while (block_cnt> 0) {
			size = op_func(start_blk_no, block_cnt, mapdir_flag,region_flag);
			start_blk_no += size;
			block_cnt-=size;
		}
		break;

	  default: 
		break;
	}
      }
}

// 因为MLC采用类似DFTL的算法，所以也有MLC_opagemap的数组
void MLC_find_real_max()
{
  int i; 

  for(i=0;i < MAP_REAL_MAX_ENTRIES; i++) {
      if(MLC_opagemap[real_arr[i]].map_age > MLC_opagemap[real_max].map_age) {
          real_max = real_arr[i];
      }
  }
}

int MLC_find_real_min()
{
  
  int i,index; 
  int temp = 99999999;

  for(i=0; i < MAP_REAL_MAX_ENTRIES; i++) {
        if(MLC_opagemap[real_arr[i]].map_age <= temp) {
            real_min = real_arr[i];
            temp = MLC_opagemap[real_arr[i]].map_age;
            index = i;
        }
  }
  return real_min;    
}

void init_arr()
{

  int i;
  for( i = 0; i < MAP_REAL_MAX_ENTRIES; i++) {
      real_arr[i] = 0;
  }
  for( i= 0; i < MAP_SEQ_MAX_ENTRIES; i++) {
	  seq_arr[i] = 0;
  }
  for( i = 0; i < MAP_SECOND_MAX_ENTRIES; i++){
	  second_arr[i] = 0;
  }
  for( i = 0; i < CACHE_MAX_ENTRIES; i++) {
      cache_arr[i] = -1;
  }
 /*
  //设置翻译页，一个翻译页最多可以存储512个映射项
   block = (struct lru *)malloc(blocksize*sizeof(struct lru));//(struct lru *)强制类型转换
    for( i = 0; i < init_size; i++) 
    {//duchenjie: for CACHE_BLOCK
	  block[i].blknum = -1;//缓存初始块中所有的块不存储任何信息
	  block[i].age = 0;
    }*/
}

int search_table(int *arr, int size, int val) //等价于 int search_table(int arr[], int size, int val) ，形参数组名作为指针变量来处理
{
    int i;
    for(i =0 ; i < size; i++) {
       if(arr[i] == val) 
      {  //if ( *(arr + i) == val )
            return i;
        }
    }

    printf("shouldnt come here for search_table()=%d,%d",val,size);
    for( i = 0; i < size; i++) {
      if(arr[i] != 0) {
        printf("arr[%d]=%d ",i,arr[i]);
      }
    }
    exit(1);
    return -1;
}


int find_free_pos( int *arr, int size)
{
    int i;
    for(i = 0 ; i < size; i++) {
        if(arr[i] == 0) {   //可以改成 if ( *(arr + i ) == 0),  arr[i]和 *(arr+i) 是两个等价
            return i;
        }
    } 
    printf("shouldn't come here for find_free_pos()\n");
    exit(1);
    return -1;
}

void find_min_cache()
{
  int i; 
  int temp = 999999;

  for(i=0; i < CACHE_MAX_ENTRIES ;i++) {
      if(MLC_opagemap[cache_arr[i]].cache_age <= temp ) {
          cache_min = cache_arr[i];
          temp = MLC_opagemap[cache_arr[i]].cache_age;
      }
  }
}

void find_MC_entries(int *arr, int size)//find_MC_entries(second_arr,MAP_SECOND_MAX_ENTRIES);
{
	int i,j,k;
	int maxCount=1;
	int temp=99999999;
  // arr1存的依次是其MVPN的编号,相同的是一个簇
	int *arr1=(int *)malloc(sizeof(int)*size);
  // 确定翻译页的arr
  // MLC_MAP_ENTRIES_PER_PAGE表示一个页能存多少个映射项
  /**************LPN (CMT:LPN--PPN) ---->   MVPN (GTD MVPN -- MPPN)**********************************/
  //换算关系 LPN/MLC_MAP_ENTRIES_PER_PAGE = MVPN
	for(k=0;k < size;k++)
		*(arr1+k)=(arr[k]-MLC_page_num_for_2nd_map_table)/MLC_MAP_ENTRIES_PER_PAGE;
	// 从尾部扫描，找到聚簇最大的簇号MPPN号，配合CPFTL的C-CMT的聚簇策略，
  // 下面的代码逻辑有大问题。。。。temp赋值变化有问题
  for(i=size-1;i>=0;i--)
	{
		if(arr1[i]!=temp)
		{
			for(j=i-1;j>=0;j--)
			{
				if(arr1[j]==arr1[i])//属于同一个簇
				{
					maxCount++;
					arr1[j]=temp;
				}
			}
			if(maxCount>=MC)
			{
				MC=maxCount;
				maxentry=arr1[i];
			}
			maxCount=1;
		}
	}
  // 最后释放中间变量
	free(arr1);

}

/******************执行的是查找CMT的目的，pageno 就是LPN***************************/
/* 在CMT 中返回 0，不在 返回1 */
int not_in_cache(unsigned int pageno)
{
	int flag = 0;
	unsigned int i;
	for(i=pageno;i<pageno+NUM_ENTRIES_PER_TIME;i++)
	{
		if((MLC_opagemap[i].map_status == 0)||(MLC_opagemap[i].map_status == MAP_INVALID))
			flag=1;
		else
		{
			flag=0;
			break;
		}
	}
	return flag;
}

//配合SDFTL，查看对应的页是否存在SL_CMT中，
/*只要块中有一个页在SL-CMT中，就返回0。块中所有的页都不在缓存中，就返回1*/
int Is_blk_in_second(int number,int num)
{
	int i; int flag1;
	for(i = number; i < (number+num); i++)
  {
	   if(MLC_opagemap[i].map_status != MAP_SECOND)
	   {
        flag1= 1;
     }
     else
     {
        flag1=0;
        break;
     }
  }//只要块中有一个页在SL-CMT中，就返回0
   return flag1;//块中所有的页都不在缓存中，就返回1
}

int youkim_flag1=0;
/******************************调用callFsim函数的地方区分判断写入SLC还是MLC的判断***************************/
double callFsim(unsigned int secno, int scount, int operation,int flash_flag,int region_flag)
{
  double delay; 
  int bcount;
  unsigned int blkno; // pageno for page based FTL
  int cnt,z; int min_real;
  int pos=-1,pos_real=-1,pos_ghost=-1,pos_2nd=-1;
  
  if(ftl_type == 1){ }

  if(ftl_type == 3) {
      // MLC的页大小不同，导致翻译页总数不同
      MLC_page_num_for_2nd_map_table = (MLC_opagemap_num / MLC_MAP_ENTRIES_PER_PAGE);
      if(youkim_flag1 == 0 ) {
        youkim_flag1 = 1;
        init_arr();
      }
//    如果不是整除，则需要增加额外的翻译页数目MVPN
      if((MLC_opagemap_num % MLC_MAP_ENTRIES_PER_PAGE) != 0){
        MLC_page_num_for_2nd_map_table++;
      }
  }
      
  // page based FTL 
  if(ftl_type == 1 ) { 
    blkno = secno / 4;
    bcount = (secno + scount -1)/4 - (secno)/4 + 1;
  }  
  // block based FTL 
  else if(ftl_type == 2){
    blkno = secno/4;
    bcount = (secno + scount -1)/4 - (secno)/4 + 1;       
  }
  // o-pagemap scheme,SLC和MLC的页对齐方式不一样
  else if(ftl_type == 3 ) {
    if(flash_flag==0){ 
       blkno = secno / 4;
       bcount = (secno + scount -1)/4 - (secno)/4 + 1;
      //  用以计算向SLC写入的次数
       SLC_write_page_count+=bcount;
    }
    else{
      // MLC 4KB页，8个扇区
       blkno = secno / 8;
      //  因为只是MLC使用了SDFTL，所以其数据页的LPN上加上翻译页总量偏移数
       blkno += MLC_page_num_for_2nd_map_table;
       bcount = (secno + scount -1)/8 - (secno)/8 + 1;
    }
  }  
  // FAST scheme
  else if(ftl_type == 4){
    blkno = secno/4;
    bcount = (secno + scount -1)/4 - (secno)/4 + 1;
  }

  cnt = bcount;
  total_request_size = total_request_size + bcount;
  // 根据请求大小判断是否为大请求？
  if (bcount>2)
  {
      big_request_entry++;
      big_request_count+= bcount; 
  }
  switch(operation)
  {
    //write/read
    case 0:
    case 1:

    while(cnt > 0)
    {
          cnt--;
          // 用于itemcount_threshold统计一定周期的读写比例
          itemcount++;
        // page based FTL
        if(ftl_type == 1){
          send_flash_request(blkno*4, 4, operation, 1,0,region_flag); 
          blkno++;
        }

        // blck based FTL
        else if(ftl_type == 2){
          send_flash_request(blkno*4, 4, operation, 1,0,region_flag); 
          blkno++;
        }

        // opagemap ftl scheme
        else if(ftl_type == 3)
        {
           if(flash_flag==0){
            //  这段代码不会进入执行，因为SLC不采用SDFTL
              send_flash_request(blkno*4, 4, operation, 1,0,region_flag); 
              blkno++;
           }
           else{
            //  针对MLC进行操作
              if (itemcount<itemcount_threshold)//利用trace数进行判断
              { 
                  rqst_cnt++;
                  if(operation==0){
                      write_count++;//用于计算总的写请求数    
                  }
                  else
                    read_count++;
              }
              else{
                // 统计itemcount_threshold周期内的读写比例
                  if (itemcount==itemcount_threshold){
                      request_cnt = rqst_cnt;
                      write_cnt = write_count;
                      read_cnt = read_count;
                      write_ratio = (write_cnt*1.0)/request_cnt;//写请求比例
                      read_ratio = (read_cnt*1.0)/request_cnt;  //读请求比列 
                    
                      average_request_size = (total_request_size*1.0)/itemcount;//请求平均大小
                      //达到itemcount_threshold时候才设计一下参数？ 
                      MAP_REAL_MAX_ENTRIES=4096;
                      real_arr=(int *)malloc(sizeof(int)*MAP_REAL_MAX_ENTRIES);
                      //MAP_GHOST_MAX_ENTRIES=822;
                      //ghost_arr=(int *)malloc(sizeof(int)*MAP_GHOST_MAX_ENTRIES);
                      MAP_SEQ_MAX_ENTRIES=1536; 
                      seq_arr=(int *)malloc(sizeof(int)*MAP_SEQ_MAX_ENTRIES); 
                      MAP_SECOND_MAX_ENTRIES=2560; 
                      second_arr=(int *)malloc(sizeof(int)*MAP_SECOND_MAX_ENTRIES); 
                      init_arr();        
                    }

                    rqst_cnt++;
                      //duchenjie:no ghost
                      // blkno就是pageno!!!!,首先判断请求的数据页映射关系是否存在CMT中的（real）
                    if (MLC_opagemap[blkno].map_status == MAP_REAL){
                        Hit_In_CMT(&blkno,operation,region_flag);
		                    continue;
                    }
                      // 请求的数据页映射关系存在S-CMT,或者在SL-CMT中
                    else if((MLC_opagemap[blkno].map_status == MAP_SEQ)||(MLC_opagemap[blkno].map_status == MAP_SECOND)){
                          Hit_In_SCMT_Or_SLCMT(&blkno,operation,region_flag);
                            continue;
                    }  //3.shzb:连续请求加入连续缓存中
                    else if((cnt+1) >= THRESHOLD){
                        // 请求大小大于一定阈值判断为连续请求，预取模式
                        //shzb:THRESHOLD=2,表示大于或等于4KB的请求，当作连续请求来处理。
                        // 直接查看CMT查看是否存在该映射关系
                            S_CMT_pre_load(&blkno,&cnt,operation,region_flag);
                            continue;
                    }else{
                        //4. opagemap not in SRAM ,处理非连续模式请求,CMT满操作
                        //if map table in SRAM is full
                        if((MAP_REAL_MAX_ENTRIES - MAP_REAL_NUM_ENTRIES) == 0)
                        {
                            CMT_Is_Full(region_flag);
                        }
//                      第一次加载的翻译页中映射项加载到CMT中
                        Load_MVPN_from_flash(blkno,region_flag);
//                       根据映射项读写数据页
                        if(operation==0){
                            write_count++;
                            MLC_opagemap[blkno].update = 1;
                        } else
                            read_count++;
                        send_flash_request(blkno*8, 8, operation, 1,1,region_flag);
                    }
                }
               // 统计itemcount_threshold周期内的读写比例
                blkno++;
       }
    }
    // FAST scheme
    else if(ftl_type == 4){
        if(operation == 0){
            write_count++;
          }
        else read_count++;
        send_flash_request(blkno*4, 4, operation, 1,0,region_flag); //cache_min is a page for page baseed FTLblkno++;//
     }
    }//end-while
    break;
  }

//  计算时延
  if(flash_flag==0){
     delay = calculate_delay_SLC_flash();
  }
  else{
     delay = calculate_delay_MLC_flash();
  }

  return delay;
}

// 封装一部分函数
void Hit_In_CMT(int *pageno,int operation,int region_flag)
{
    cache_cmt_hit++;
    MLC_opagemap[*pageno].map_age = MLC_opagemap[real_max].map_age + 1;//LRU
    real_max = *pageno;
    if(MLC_opagemap[real_max].map_age <= MLC_opagemap[*pageno].map_age){
        real_max = *pageno;
    }  
    // 如果请求是写请求，该映射关系一定发生更新了，对应的opagemap[*pageno].update要置位
    if(operation==0){
        write_count++;
        MLC_opagemap[*pageno].update = 1;
    }
    else
        read_count++;
    send_flash_request(*pageno*8, 8, operation, 1,1,region_flag); 
    (*pageno)++;
}

// 关于状态的重置
void MLC_opagemap_Invalid(int pageno)
{
    MLC_opagemap[pageno].update = 0;
    MLC_opagemap[second_arr[pageno]].map_status = MAP_INVALID;
    MLC_opagemap[second_arr[pageno]].map_age = 0;
}

/*
* 当CMT满的时候将更新后的映射项移动到SLCMT中，没有更新的直接移除
* second_arr是SLCMT的表现形式，存放的是对应映射想的DLPN
* real_arr对应的是CMT，存放的也是对应映射项的DLPN
*/
void CMT_Is_Full(int region_flag)
{
        // 根据LRU原则，选择age最小的剔除，如果update更新过，剔除到SL-CMT
        int min_real;
        int pos=-1,pos_2nd=-1;
        min_real = MLC_find_real_min();
        if(MLC_opagemap[min_real].update == 1){
        // 如果SL-CMT满，则执行最大关联度的簇进行批量更新 
            if((MAP_SECOND_MAX_ENTRIES-MAP_SECOND_NUM_ENTRIES) ==0){
                MC=0;
                // 找到SL-CMT中聚簇最大的翻译页回写更新翻译页,maxentry是个全局变量
                find_MC_entries(second_arr,MAP_SECOND_MAX_ENTRIES);
                // maxentry就是该翻译页的MVPN号，翻译页的更新，先读再写入操作
                send_flash_request(maxentry*8,8,1,2,1,region_flag);
                translation_read_num++;
                send_flash_request(maxentry*8,8,0,2,1,region_flag);
                translation_write_num++;
                // 将更新的剔除的映射项加入到SL-CMT中
                // 将对应的opagemap中的状态置位
                for(indexold = 0;indexold < MAP_SECOND_MAX_ENTRIES; indexold++){
                    if(((second_arr[indexold]-MLC_page_num_for_2nd_map_table)/MLC_MAP_ENTRIES_PER_PAGE) == maxentry){
                      			MLC_opagemap[second_arr[indexold]].update = 0;
                            MLC_opagemap[second_arr[indexold]].map_status = MAP_INVALID;
                            MLC_opagemap[second_arr[indexold]].map_age = 0;
                            second_arr[indexold]=0;
						                MAP_SECOND_NUM_ENTRIES--;
                    }
                }
            }
            // 将从CMT中剔除的数据页转移到SL-CMT中
            MLC_opagemap[min_real].map_status = MAP_SECOND;
            // 读出该映射关系，将CMT（real_arr）中的位置置位空位置
            pos = search_table(real_arr,MAP_REAL_MAX_ENTRIES,min_real);
            real_arr[pos]=0;
            MAP_REAL_NUM_ENTRIES--;
            // 将映射内容加载到SL-CMT中
            pos_2nd = find_free_pos(second_arr,MAP_SECOND_MAX_ENTRIES);
            second_arr[pos_2nd]=0;
            second_arr[pos_2nd]=min_real;
            MAP_SECOND_NUM_ENTRIES++;
            if(MAP_SECOND_NUM_ENTRIES > MAP_SECOND_MAX_ENTRIES){
                    printf("The second cache is overflow!\n");
                    exit(0);
            }       
        }
        else{  // 如果剔除的映射项不是更新项，直接剔除即可
                pos = search_table(real_arr,MAP_REAL_MAX_ENTRIES,min_real);
                real_arr[pos]=0;
                MLC_opagemap[min_real].map_status = MAP_INVALID;
				        MLC_opagemap[min_real].map_age = 0;
                MAP_REAL_NUM_ENTRIES--;
        }
}

// 请求的映射关系存在SCMT或者在SLCMT中,一些相关统计变量应该是全局的后面核实下
void Hit_In_SCMT_Or_SLCMT(int *pageno,int operation,int region_flag)
{
    //2.shzb:请求在连续缓存(S-CMT)中,连续命中的请求，直接加载到CMT中
    //			cache_hit++;
    int pos = -1;
    if(MLC_opagemap[*pageno].map_status == MAP_SEQ){
        cache_scmt_hit++;
        MLC_opagemap[*pageno].map_age++;
        if(MLC_opagemap[*pageno].map_age >1){
            if((MAP_REAL_MAX_ENTRIES - MAP_REAL_NUM_ENTRIES) == 0){
            //把该映射项加载到real中，处理Real LRU满的时候剔除操作
                CMT_Is_Full(region_flag);
            }
            // CMT（腾出新的空间）,将SL-CMT的映射项导入CMT中
            MLC_opagemap[*pageno].map_status = MAP_REAL;
            MLC_opagemap[*pageno].map_age = MLC_opagemap[real_max].map_age + 1;
            real_max = *pageno;      
            pos = find_free_pos(real_arr,MAP_REAL_MAX_ENTRIES);
            real_arr[pos] = 0;
            real_arr[pos] = pageno;
            MAP_REAL_NUM_ENTRIES++;
        }
    }
    else{
        //在SL-CMT中，不做任何映射项的处理
        cache_slcmt_hit++;
        //opagemap[pageno].map_age++;   
    }
    // 对应的映射项的更新（写请求）
    if(operation==0){
        write_count++;
        MLC_opagemap[*pageno].update = 1;
        }
    else
        read_count++;
    // 根据实际的数据页请求，处理闪存，实际的请求在哪逻辑
    // lpn --- ppn的映射逻辑 怎么实现？
    send_flash_request((*pageno)*8, 8, operation, 1,1,region_flag); 
    pageno++;
}


//// 如果S-CMT满，则根据FIFO策略剔除 ,seq_arr其实是个全局变量不需要进行传值输入
void SCMT_Is_Full(int region_flag)
{
      for(indexofarr = 0;indexofarr < NUM_ENTRIES_PER_TIME;indexofarr++)
      {//SEQ的替换策略是把SEQ的最前面几个映射项剔除出去
          if((MLC_opagemap[seq_arr[indexofarr]].update == 1)&&(MLC_opagemap[seq_arr[indexofarr]].map_status == MAP_SEQ))
          {
            //update_reqd++;,update_flag是个全局变量
            update_flag=1;
            MLC_opagemap[seq_arr[indexofarr]].update=0;
            MLC_opagemap[seq_arr[indexofarr]].map_status = MAP_INVALID;
            MLC_opagemap[seq_arr[indexofarr]].map_age = 0;
          }
          else if((MLC_opagemap[seq_arr[indexofarr]].update == 0)&&(MLC_opagemap[seq_arr[indexofarr]].map_status ==MAP_SEQ))
          {
            MLC_opagemap[seq_arr[indexofarr]].map_status = MAP_INVALID;
            MLC_opagemap[seq_arr[indexofarr]].map_age = 0;
          }
      }

      if(update_flag == 1)
      {
        send_flash_request(((seq_arr[0]-MLC_page_num_for_2nd_map_table)/MLC_MAP_ENTRIES_PER_PAGE)*8,8,1,2,1,region_flag);
        translation_read_num++;
        send_flash_request(((seq_arr[0]-MLC_page_num_for_2nd_map_table)/MLC_MAP_ENTRIES_PER_PAGE)*8,8,0,2,1,region_flag);
        translation_write_num++;
        update_flag=0;
      }

      for(indexofarr = 0;indexofarr <= MAP_SEQ_MAX_ENTRIES-1-NUM_ENTRIES_PER_TIME; indexofarr++)
        seq_arr[indexofarr] = seq_arr[indexofarr+NUM_ENTRIES_PER_TIME];
      
      MAP_SEQ_NUM_ENTRIES-=NUM_ENTRIES_PER_TIME;
}


//该函数主要完成预取策略

void S_CMT_pre_load(int *pageno,int *cnt,int operation,int region_flag)
{
    if(not_in_cache(*pageno)){
        MLC_opagemap[*pageno].map_age++;
        // 如果S-CMT满，则根据FIFO策略剔除
        if((MAP_SEQ_MAX_ENTRIES-MAP_SEQ_NUM_ENTRIES)==0){
            SCMT_Is_Full(region_flag);
        }
//            flash_hit???请求一次flash的读写?
        flash_hit++;
//            一次预取的数据页从底层读取一个翻译页
        send_flash_request(((*pageno-MLC_page_num_for_2nd_map_table)/MLC_MAP_ENTRIES_PER_PAGE)*8, 8, 1, 2,1,region_flag);
        translation_read_num++;
        //NUM_ENTRIES_PER_TIME在这里表示一次加载4个映射表信息
        for(indexofseq=0; indexofseq < NUM_ENTRIES_PER_TIME;indexofseq++)
        {
            MLC_opagemap[*pageno+indexofseq].map_status=MAP_SEQ;
            seq_arr[MAP_SEQ_NUM_ENTRIES] = (*pageno+indexofseq);//加载到SEQ的映射项是放在SEQ尾部
            MAP_SEQ_NUM_ENTRIES++;
        }

        if(MAP_SEQ_NUM_ENTRIES > MAP_SEQ_MAX_ENTRIES)
        {
            printf("The sequential cache is overflow!\n");
            exit(0);
        }
//          单次数据页的更新操作
        if(operation==0)
        {
            write_count++;
            MLC_opagemap[*pageno].update=1;
        }
        else
            read_count++;
        send_flash_request(*pageno*8,8,operation,1,1,region_flag);

       (*pageno)++;
//       sequential_count  全局变量不需要传值进来
        sequential_count = 0;
//           连续加载映射项到CMT中
        for(;(*cnt>0)&&(sequential_count<NUM_ENTRIES_PER_TIME-1);(*cnt)--)
        {
            MLC_opagemap[*pageno].map_age++;
            cache_scmt_hit++;
            if(operation==0)
            {
                write_count++;
                MLC_opagemap[*pageno].update=1;
            }
            else
                read_count++;
            send_flash_request(*pageno*8,8,operation,1,1,region_flag);
            (*pageno)++;
            rqst_cnt++;
            sequential_count++;
        }
//        就是外层函数终止
    }
    else{
//            处于连续访问请求的数据页需要加载到CMT中,CMT满需要剔除操作
        if((MAP_REAL_MAX_ENTRIES - MAP_REAL_NUM_ENTRIES) == 0){
            // 代码重复，做了删减，当SCMT连续访问中的映射项二次命中也会加载到CMT中，此时CMT满也执行一样的置换操作
            CMT_Is_Full(region_flag);
        }
        Load_MVPN_from_flash((*pageno),region_flag);
//        映射项处理完,更新对应的数据页的更新
        if(operation==0){
            write_count++;
            MLC_opagemap[*pageno].update = 1;
        }
        else
            read_count++;
        send_flash_request(*pageno*8, 8, operation, 1,1,region_flag);
        *pageno++;
    }
}

//从flash中读取翻译页(MVPN)加载到CMT中,real_max和real_arr都是全局变量
void Load_MVPN_from_flash(int pageno,int region_flag)
{
    //read MVPN from flash
    int pos = -1;
    flash_hit++;
    send_flash_request(((pageno-MLC_page_num_for_2nd_map_table)/MLC_MAP_ENTRIES_PER_PAGE)*8, 8, 1, 2,1,region_flag);   // read from 2nd mapping table
    translation_read_num++;
    MLC_opagemap[pageno].map_status = MAP_REAL;
    MLC_opagemap[pageno].map_age = MLC_opagemap[real_max].map_age + 1;
    real_max = pageno;
    pos = find_free_pos(real_arr,MAP_REAL_MAX_ENTRIES);//因为real_arr已经被定义成指针变量，调用find_free_pos函数时前面不需要加*
    real_arr[pos] = 0;
    real_arr[pos] = pageno;
    MAP_REAL_NUM_ENTRIES++;
}

